package com.loren.oauth.config;

import com.loren.oauth.converter.CustomJwtAccessTokenConverter;
import com.loren.oauth.granter.SmsCodeAuthenticationProvider;
import com.loren.oauth.granter.SmsCodeTokenGranter;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Configuration
@AllArgsConstructor
@EnableAuthorizationServer
public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {

	private final DataSource dataSource;

	private final AuthenticationManager authenticationManager;

	private final UserDetailsService userDetailsService;

	@Bean
	public TokenStore tokenStore() {
		return new JwtTokenStore(jwtTokenEnhancer());
	}

	@Bean
	public JwtAccessTokenConverter jwtTokenEnhancer() {
		JwtAccessTokenConverter converter = new CustomJwtAccessTokenConverter();
		ClassPathResource keyStorePath = new ClassPathResource("loren.keystore");
		KeyStoreKeyFactory keyFactory = new KeyStoreKeyFactory(keyStorePath, "lsy_loren".toCharArray());
		converter.setKeyPair(keyFactory.getKeyPair("loren"));
		return converter;
	}

	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) {
		security.tokenKeyAccess("isAuthenticated()").checkTokenAccess("isAuthenticated()");
	}

	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.jdbc(this.dataSource);
	}

	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
		endpoints
			.tokenGranter(getCustomGranter(endpoints))
			.userDetailsService(this.userDetailsService)
			.tokenStore(tokenStore())
			.tokenEnhancer(jwtTokenEnhancer())
			.authenticationManager(this.authenticationManager);
	}

	private TokenGranter getCustomGranter(AuthorizationServerEndpointsConfigurer endpoints) {
		List<AuthenticationProvider> providerList = new ArrayList<>();
		SmsCodeAuthenticationProvider provider = new SmsCodeAuthenticationProvider();
		provider.setUserDetailsService(this.userDetailsService);
		providerList.add(provider);
		List<TokenGranter> tokenGranters = new ArrayList<>(
				Collections.singletonList(new SmsCodeTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(),
						endpoints.getOAuth2RequestFactory(), new ProviderManager(providerList))));
		tokenGranters.add(endpoints.getTokenGranter());
		return new CompositeTokenGranter(tokenGranters);
	}
}
